package com.byq.mm.smartlib.engine;

import java.io.File;
import java.io.FileOutputStream;
import java.sql.Date;
import java.sql.Time;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Stack;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;



import com.byq.mm.abstractx.cluster.server.ClientEntry;
import com.byq.mm.abstractx.cluster.server.ServerIdentifier;
import com.byq.mm.abstractx.device.Cluster;
import com.byq.mm.abstractx.device.Device;
import com.byq.mm.abstractx.engine.AccountManager;
import com.byq.mm.abstractx.engine.CoreStore;
import com.byq.mm.abstractx.protocol.Interaction;
import com.byq.mm.common.engine.SmartStore;
import com.byq.mm.smartlib.activity.Constants;
import com.byq.mm.smartlib.data.SmartDbConnector;


public class LiteStore implements SmartStore, CoreStore {
	AccountManager account;
	Context ctx;

	public LiteStore(Context ctx) {
		super();
		this.ctx = ctx;
		account = new AndroidAccountManager(ctx);
	}


	public Device findDevice(String globalId) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getReadableDatabase();

		Device rst = findDevice(globalId, db);
		extractChildren(db, rst);

		db.close();
		return rst;
	}

	private void extractChildren(SQLiteDatabase db, Device rst) {
		Queue<Device> queue = new LinkedList<Device>();
		queue.offer(rst);
		while (!queue.isEmpty()) {

				Device d = (Device) queue.poll();
				Collection<Device> cs = findChildren(d.getGlobalId(), db);
				d.setSubdevices(cs);
				for (Device c : cs)
					queue.offer(c);

			
		}
	}

	public Collection<Device> findRootDevices() {
		SQLiteDatabase db = new SmartDbConnector(ctx).getReadableDatabase();
		Collection<Device> rst = findChildren("empty", db);
		for (Device d : rst)
			extractChildren(db, d);
		db.close();
		return rst;

	}
	Collection<Cluster> getClusters(String globalId, SQLiteDatabase db){
		String table = Constants.ClusterEntry.TABLE_NAME;
		String[] columns = { 
				Constants.ClusterEntry.ID, 
				Constants.ClusterEntry.ALIAS

				
				};
		String selection = Constants.ClusterEntry.DEVICEID + " = ?";
		String[] selectionArgs = new String[] { globalId };

		Cursor result = db.query(table, columns, selection, selectionArgs,
				null, null, null);

		result.moveToFirst();

		List<Cluster> de = new ArrayList<Cluster>();
		while (!result.isAfterLast()) {

			Cluster d = new Cluster();
			d.setId(result
					.getString(result.getColumnIndex(Constants.ClusterEntry.ID)));
			d.setAlias(result.getString(result
					.getColumnIndex(Constants.ClusterEntry.ALIAS)));
			
			de.add(d);
			result.moveToNext();
		}

		result.close();
		return de;
	}
	
	void deleteClusters(String globalId, SQLiteDatabase db){


		String selection = Constants.ClusterEntry.DEVICEID + " = ?";
		String[] selectionArgs = new String[] { globalId };

		db.delete(
				Constants.ClusterEntry.TABLE_NAME, 
				selection, selectionArgs);

	}
	Device findDevice(String globalId, SQLiteDatabase db) {
		String table = Constants.Device.TABLE_NAME;
		String[] columns = { 
				Constants.Device.ID,
				Constants.Device.TRANSPORTID,
				Constants.Device.NAME, 
				Constants.Device.TYPE 
				};
		String selection = Constants.Device.GLOBALID + " = ?";
		String[] selectionArgs = new String[] { globalId };
		Cursor result = db.query(table, columns, selection, selectionArgs,
				null, null, null);

		result.moveToFirst();
		Device d = new Device();

		d.setId(result.getString(result.getColumnIndex(Constants.Device.ID)));
		
		
		d.setTansportId(result.getString(result
				.getColumnIndex(Constants.Device.TRANSPORTID)));
		d.setName(result.getString(result.getColumnIndex(Constants.Device.NAME)));
		d.setType(result.getString(result.getColumnIndex(Constants.Device.TYPE)));
		result.close();
		Collection<Cluster> cs = getClusters(globalId, db);
		d.setSupportedClusters(cs);
		return d;

	}

	Collection<Device> findChildren(String parent, SQLiteDatabase db) {

		String table = Constants.Device.TABLE_NAME;
		String[] columns = { 
				Constants.Device.ID, 
				Constants.Device.TRANSPORTID,
				Constants.Device.NAME,
				Constants.Device.TYPE 
				};
		String selection = Constants.Device.PARENT + " = ?";
		String[] selectionArgs = new String[] { parent };

		Cursor result = db.query(table, columns, selection, selectionArgs,
				null, null, null);

		result.moveToFirst();

		List<Device> de = new ArrayList<Device>();
		while (!result.isAfterLast()) {

			Device d = new Device();
			d.setId(result
					.getString(result.getColumnIndex(Constants.Device.ID)));
			d.setTansportId(result.getString(result
					.getColumnIndex(Constants.Device.TRANSPORTID)));
			d.setName(result.getString(result
					.getColumnIndex(Constants.Device.NAME)));
			d.setType(result.getString(result
					.getColumnIndex(Constants.Device.TYPE)));
			Collection<Cluster> cs = getClusters(d.getGlobalId(), db);
			d.setSupportedClusters(cs);
			de.add(d);
			result.moveToNext();
		}

		result.close();
		return de;
	}

	public void deleteDevice(Device device) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();

		Stack<Device> stack = new Stack<Device>();
		Queue<Device> queue = new LinkedList<Device>();
		queue.offer(device);
		while (!queue.isEmpty()) {
	
				Device d = (Device) queue.poll();
				stack.push(d);
				for (Device c : d.getSubdevices())
					queue.offer(c);
			

		}
		while(!stack.isEmpty()){
			Device d = stack.pop();
			deleteClusters(d.getGlobalId(), db);
			db.delete(Constants.Device.TABLE_NAME, Constants.Device.GLOBALID + " = ?", new String[]{d.getGlobalId()});
		}
		db.close();
	}

	

	public void saveDevice(Device device) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();

		saveDevice(db, device, "empty");

		db.close();
	}

	void saveDevice(SQLiteDatabase db, Device device, String parent) {

		ContentValues val = new ContentValues();
		val.put(Constants.Device.ID, device.getId());
		val.put(Constants.Device.GLOBALID, device.getGlobalId());
		val.put(Constants.Device.PARENT, parent);
		val.put(Constants.Device.TRANSPORTID, device.getTransportId());
		val.put(Constants.Device.NAME, device.getName());
		val.put(Constants.Device.TYPE, device.getType());

		db.insert(Constants.Device.TABLE_NAME, null, val);
		saveClusters(db, device);
		for (Device c : device.getSubdevices()) {
			saveDevice(db, c, device.getGlobalId());
		}

	}
	void saveClusters(SQLiteDatabase db,Device device ){
		ContentValues val = new ContentValues();
		for(Cluster c : device.getSupportedClusters()){
			val.clear();

		val.put(Constants.ClusterEntry.ID, c.getId());
		val.put(Constants.ClusterEntry.ALIAS, c.getAlias());
		val.put(Constants.ClusterEntry.DEVICEID, device.getGlobalId());
		
		db.insert(Constants.ClusterEntry.TABLE_NAME, null, val);
		}
	}

	public void queueIncomingMessage(Map<String, String> msgInfo) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();

		long time = Long.parseLong(msgInfo.get("time"));

		ContentValues val = new ContentValues();

		if (account.logMessageKey()) {
			val.put(Constants.TABLE_LOG_FIELD_DATE, new Date(time).toString());
			val.put(Constants.TABLE_LOG_FIELD_TIME, new Time(time).toString());
			val.put(Constants.TABLE_LOG_FIELD_FROM, msgInfo.get("user"));
			val
					.put(Constants.TABLE_LOG_FIELD_RESOURCE, msgInfo
							.get("resource"));
			val.put(Constants.TABLE_LOG_FIELD_MSG, msgInfo.get("body").trim());
			db.insert(Constants.TABLE_LOG, null, val);
		}

		val.clear();
		val.put(Constants.Conversation.DATE, time);
		val.put(Constants.Conversation.CHAT, msgInfo.get("user"));
		val.put(Constants.Conversation.FROM, msgInfo.get("user"));
		val.put(Constants.Conversation.TO, "me");
		val.put(Constants.Conversation.MSG, msgInfo.get("body").trim());
		val.put(Constants.Conversation.NEW, 1);
		db.insert(Constants.Conversation.TABLE_NAME, null, val);

		db.close();
	}

	public void logOutgoingMessage(final String body, final String to,
			String user) {

		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();

		int slash = user.lastIndexOf("/");
		String resource = user.substring(slash + 1);
		user = user.substring(0, slash);

		long time = System.currentTimeMillis();

		ContentValues val = new ContentValues();

		if (account.logMessageKey()) {
			val.put(Constants.TABLE_LOG_FIELD_DATE, new Date(time).toString());
			val.put(Constants.TABLE_LOG_FIELD_TIME, new Time(time).toString());
			val.put(Constants.TABLE_LOG_FIELD_FROM, user);
			val.put(Constants.TABLE_LOG_FIELD_RESOURCE, resource);
			val.put(Constants.TABLE_LOG_FIELD_MSG, body.trim());
			db.insert(Constants.TABLE_LOG, null, val);
		}

		val.clear();
		val.put(Constants.Conversation.DATE, time);
		val.put(Constants.Conversation.CHAT, to);
		val.put(Constants.Conversation.FROM, "me");
		val.put(Constants.Conversation.TO, to);
		val.put(Constants.Conversation.MSG, body.trim());
		val.put(Constants.Conversation.NEW, 0);
		db.insert(Constants.Conversation.TABLE_NAME, null, val);

		db.close();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.byq.mm.engine.SmartStore#getLastStatusMessages()
	 */
	public List<String> getLastStatusMessages() {
		SQLiteDatabase db = new SmartDbConnector(ctx).getReadableDatabase();

		String table = Constants.TABLE_STATUSMSG;
		String[] columns = { Constants.TABLE_STATUSMSG_FIELD_MSG };
		String orderBy = Constants.TABLE_STATUSMSG_FIELD_LASTUSED
				+ " DESC LIMIT 10";
		// String where = Constants.TABLE_STATUSMSG_FIELD_MSG + " != ''";

		Cursor result = db.query(table, columns, null, null, null, null,
				orderBy);

		result.moveToFirst();

		List<String> messages = new ArrayList<String>();
		while (!result.isAfterLast()) {
			messages.add(result.getString(result
					.getColumnIndex(Constants.TABLE_STATUSMSG_FIELD_MSG)));
			result.moveToNext();
		}

		result.close();
		db.close();

		return messages;
	}

	@Override
	public void pushMessage(String message) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getReadableDatabase();

		String table = Constants.TABLE_STATUSMSG;

		ContentValues cv = new ContentValues();

		cv.put(Constants.TABLE_STATUSMSG_FIELD_ACTIVE, false);
		db.update(table, cv, null, null);

		cv.clear();
		cv.put(Constants.TABLE_STATUSMSG_FIELD_MSG, message);
		cv.put(Constants.TABLE_STATUSMSG_FIELD_ACTIVE, true);
		cv.put(Constants.TABLE_STATUSMSG_FIELD_LASTUSED, new java.util.Date()
				.getTime());
		db.insert(table, null, cv);
		db.close();
	}

	@Override
	public File getUserDirectory(String globalId) {
		if (!Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED))
			return null;
		File dir = prepare(globalId);
		return dir;
	}

	static public String save(byte[] data, String fn, String to) {

		if (!Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED))
			return null;

		File dir = prepare(to);
		File file = new File(dir, fn);

		FileOutputStream out;
		try {
			out = new FileOutputStream(file);
			out.write(data);
			return file.toURL().toString();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}

	private static File prepare(String to) {
		File dir = new File(Environment.getExternalStorageDirectory(),
				"smartagent");
		if (!dir.exists())
			dir.mkdir();
		dir = new File(dir, "users");
		if (!dir.exists())
			dir.mkdir();
		dir = new File(dir, to);
		if (!dir.exists())
			dir.mkdir();
		dir = new File(dir, "picture");
		if (!dir.exists())
			dir.mkdir();
		return dir;
	}

	static public File[] listPictures(String deviceId) {
		File pd = prepare(deviceId);
		File[] fs = pd.listFiles();
		return fs;

	}

	@Override
	public Collection<ClientEntry> findClientEntrys(String deviceId,
			String clusterId) {
		SQLiteDatabase xb = new SmartDbConnector(ctx).getReadableDatabase();
		String[] cm = new String[]{
			Constants.ClientEntry.CLIENTTRANSPORT,
			Constants.ClientEntry.PERMITTED
		};
		String sel = Constants.ClientEntry.DEVICEID + " = ? and " + Constants.ClientEntry.CLUSTERID + " = ?";
		String[] args = new String[]{
			deviceId,clusterId	
		};
		Cursor cursor = xb.query(Constants.ClientEntry.TABLE_NAME, cm, sel, args, null, null, null);
		cursor.moveToFirst();
		
		List<ClientEntry> rst = new LinkedList<ClientEntry>();
		while (!cursor.isAfterLast()) {
			ClientEntry e = new ClientEntry();
			
			e.clientTransport = cursor.getString(cursor
					.getColumnIndex(Constants.ClientEntry.CLIENTTRANSPORT));

			int  p =  cursor.getInt((cursor
					.getColumnIndex(Constants.ClientEntry.PERMITTED)));
			e.permitted = p != 0;
			rst.add(e);
			cursor.moveToNext();
		}
		cursor.close();
		xb.close();
		return rst;
	}
	@Override
	public boolean existPermission(ServerIdentifier id) {
		SQLiteDatabase xb = new SmartDbConnector(ctx).getReadableDatabase();
		String[] cm = new String[]{
		
			Constants.ServerSecurity.PERMITTED
		};
		String sel = Constants.ServerSecurity.ID + " = ?";
		String[] args = new String[]{
				id.value()
		};
		Cursor cursor = xb.query(Constants.ServerSecurity.TABLE_NAME, cm, sel, args, null, null, null);
		cursor.moveToFirst();

		boolean rst = cursor.getCount() >0;
		cursor.close();
		xb.close();
		return 		rst;
	}
	@Override
	public boolean findPermission(ServerIdentifier id) {
		SQLiteDatabase xb = new SmartDbConnector(ctx).getReadableDatabase();
		String[] cm = new String[]{
		
			Constants.ServerSecurity.PERMITTED
		};
		String sel = Constants.ServerSecurity.ID + " = ?";
		String[] args = new String[]{
				id.value()
		};
		Cursor cursor = xb.query(Constants.ServerSecurity.TABLE_NAME, cm, sel, args, null, null, null);
		cursor.moveToFirst();

			int  p =  cursor.getInt((cursor
					.getColumnIndex(Constants.ServerSecurity.PERMITTED)));
			
		cursor.close();
		xb.close();
		return 		p != 0;
	}
	String getClientEntryId(String deviceId, String clusterId, String transport){
		return "[" + deviceId + "]" + "[" + clusterId + "]" + "[" + transport + "]";
	}
	String getServerId(String deviceId, String clusterId){
		return "[" + deviceId + "]" + "[" + clusterId + "]";
	}
	@Override
	public void saveClientEntry(String deviceId, String clusterId, ClientEntry e) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();

		
		ContentValues val = new ContentValues();
		val.put(Constants.ClientEntry.ID, getClientEntryId(deviceId,clusterId,e.clientTransport));
		val.put(Constants.ClientEntry.DEVICEID, deviceId);
		val.put(Constants.ClientEntry.CLUSTERID, clusterId);
		val.put(Constants.ClientEntry.CLIENTTRANSPORT, e.clientTransport);
		val.put(Constants.ClientEntry.PERMITTED,e.permitted);
		

		db.insert(Constants.ClientEntry.TABLE_NAME, null, val);


		db.close();
	}
	@Override
	public void savePermission(ServerIdentifier id, boolean permit) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();

		
		ContentValues val = new ContentValues();
		val.put(Constants.ServerSecurity.ID, id.value());
		val.put(Constants.ServerSecurity.DEVICEID, id.getDeviceId());
		val.put(Constants.ServerSecurity.CLUSTERID,id.getClusterId());

		val.put(Constants.ServerSecurity.PERMITTED,permit);
		
		
		db.insert(Constants.ServerSecurity.TABLE_NAME, null, val);


		db.close();
	}

	@Override
	public void updateClientEntry(String deviceId, String clusterId,
			ClientEntry ce) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();
		String id = getClientEntryId(deviceId,clusterId,ce.clientTransport);
		String where = "id = ?";
		String[] args = new String[]{id};
		
		ContentValues val = new ContentValues();

		val.put(Constants.ClientEntry.PERMITTED,ce.permitted);
		

		db.update(Constants.ClientEntry.TABLE_NAME, val, where, args);


		db.close();
		
	}

	@Override
	public void acceptInteraction(Interaction i, String from) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void refuseInteraction(Interaction i, String from) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void updatePermission(ServerIdentifier id, boolean permitted) {
		SQLiteDatabase db = new SmartDbConnector(ctx).getWritableDatabase();
		
		String where = "id = ?";
		String[] args = new String[]{id.value()};
		
		ContentValues val = new ContentValues();

		val.put(Constants.ServerSecurity.PERMITTED,permitted);
		

		db.update(Constants.ServerSecurity.TABLE_NAME, val, where, args);


		db.close();
		
	}
}
